/** TESTING! L'ID host viene passato come parametro. Esempio-> java NEDiscovery 15 **/
import java.net.*;
import java.io.IOException;
import java.util.*; //For Timer 
import java.io.File;


public class NED {
		//TODO In laboratorio l'IP sarà diverso
        static String BROADCAST = "192.168.1.255";
        final static int UDP_PORT=9000;  //MUST be the same. Only for testing
        final static int UDP_PORT_ACK=9001;
        final static int UDP_PORT_LEFT=9002;
        final static int UDP_PORT_RIGHT=9003;
        
        final static boolean AVLFLAG=true;
        
        //Timers
        //static Timer timer1;
        static Timer timer3,timer2;
        //ID of ACK TXnode
        //static int idTx; 
        //static int idTx1;
        //static int idTx2;
        
        //Node entity
        static Node ME;
        
        //UDP socket
        static DatagramSocket txSocket;
        static DatagramSocket rcvSocket;
        static DatagramSocket rcvSocketACK;
        static DatagramSocket rcvSocketLeft;
        static DatagramSocket rcvSocketRight;
        
        //received UDP data buffer
        static byte[] receiveData = new byte[1024]; 
             
        //Flags
        static boolean configured=false;
        static boolean riconf=false;
        static int root_attempt=0;
        
        
        static int ID=0;
        
        public static void main(String[] args) throws Exception {
            boolean printedIP=false;
			
				//Check if ID is indicated
            //    if (args.length<1){
            //    	System.out.println("Host ID must be indicated");
             //   	System.exit(1);
             //   }
               
                if(args.length<1){
                try{
             
					//Lettura dal file
					File fileName = new File("config");  
					Scanner in = new Scanner(fileName);
					String line=in.nextLine();
					
					ID=Integer.parseInt(line);
					
					BROADCAST=in.nextLine();
				//	System.out.println(a);
				//	a="";
					//Copia valori rimasti. ///Memorizza in a
					/*while(in.hasNext()){
						a=a+in.next()+" ";
					}*/
					in.close();	
					//Scrittura valori rimasti, in file
				/*	OutputStream outputStream = new FileOutputStream(args[0]);
					Writer writer= new OutputStreamWriter(outputStream);
					
					writer.write(a);
					writer.close();*/
				}
				catch(Exception e){
					System.out.println("File vuoto");
					System.exit(1);
				}}else{
					ID=Integer.parseInt(args[0]);
				}
                if(ID>127){
                	System.out.println("Can't start host with ID>127 cause java doesn't support unsigned int");
                	System.exit(1);
                }
                //Create sockets
                txSocket = new DatagramSocket();
                rcvSocket = new DatagramSocket(UDP_PORT);
                rcvSocketACK = new DatagramSocket(UDP_PORT_ACK);
                rcvSocketLeft = new DatagramSocket(UDP_PORT_LEFT);
                rcvSocketRight = new DatagramSocket(UDP_PORT_RIGHT);
                
                //Create this node entity
                ME = new Node(txSocket,UDP_PORT, UDP_PORT_ACK,UDP_PORT_LEFT, UDP_PORT_RIGHT,BROADCAST, ID);
                ME.setIsRoot(true);
                
                //Capture machine shut-down ctrl+C
				attachShutDownHook();
	
                //Start configuring the network
                //Making and sending DSCVRY packet
                System.out.println("[Starting] Making DSCVRY packet with ID " + ME.getId());
                
                ME.sendDSCVRY();
                ME.startGateInD();
                rcvSocket.setSoTimeout(0); //set default timeout

                //Figlio controlla presenza del padre
				//Set timer1-->when timer expires send packetON and wait for ACK
				Timer timer1= new Timer();
				TimerTask task1 = new TimerTask()
				{
					public void run()
					{
						int idTx=0;
						if(!ME.isRoot()){
							try{
								ME.sendNodeOn(/*ME.getDadIp(),*/ ME.getDadId());
								//TODO TIMEOUT ACK
								rcvSocketACK.setSoTimeout(5000);
								int packetCode=0;
								while(packetCode!=7){
									DatagramPacket receivePacketACK = new DatagramPacket(receiveData, receiveData.length);                   
									rcvSocketACK.receive(receivePacketACK);
									//TODO Prendere idTx da padre
									idTx = (int) receivePacketACK.getData()[4];
									
									packetCode = (int) receivePacketACK.getData()[0];
									//Received ACK packet
									//if (packetCode == 7){
									//	System.out.println("[UDP; UDP_PORT_ACK] ACK received from ID " + idTx);
									//	break;
									//}
								}
							}catch(Exception e){
						//		System.out.println("NODO " + idTx + " IRRAGGIUNGIBILE");
								riconf=true;
								
								try {
									ME.deleteDad();
									ME.printGraph();
									ME.sendDSCVRY();
								} catch (IOException e1) {
										// TODO Auto-generated catch block
										e1.printStackTrace();
								}
								//this.cancel();
							}
							
						}
					}
				}; 
				//TODO controllo presenza del padre ogni 10 sec
				timer1.scheduleAtFixedRate(task1, 10000, 10000);	
				rcvSocketACK.setSoTimeout(0);
				rcvSocket.setSoTimeout(0);			
				
				
				
				
				
                //
                // Main loop
                //
                while (true) {
                        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                        rcvSocket.receive(receivePacket);
                        //Saving information about sender and packet received
                        InetAddress senderIP = receivePacket.getAddress();
                        int packetCode = (int) receivePacket.getData()[0];
                        int senderId = (int) receivePacket.getData()[4];
                        //System.out.println("RCV "+packetCode);

                        
                        //If packet code is not known print generic informations
                  //      if (packetCode>9)
                  //      	System.out.println("[UDP] Received from "+ senderIP.getHostAddress() + ":"+ receivePacket.getPort() + ". Code: "+ (int) receivePacket.getData()[0] + " ID: "+ (int) receivePacket.getData()[4]);
                        
                        //DSCVRY packet from ME with no configuration (First time execution)
                        if (packetCode == 0 && senderId == ME.getId() && configured==false) {
                        	ME.setIp(receivePacket.getAddress()); //set my ip
                        	if(printedIP==false)
                        		System.out.println("[Starting] This host IP: "+ ME.getIp().getHostAddress());
                        	printedIP=true;
                        }
                        
                        //IMROOT from root with no configuration (First time execution)
                        if (packetCode == 1 && senderId != ME.getId() && configured==false) {
                            System.out.println("[Found] Root found with ID " + senderId);
                            //Set root as dad
                            //ME.setDad(senderId, receivePacket.getAddress());
                            ME.setIsRoot(false);
                            //ME.sendPLCRQ(ME.getDadIp());
                            ME.sendPLCRQ(receivePacket.getAddress());
                            configured=true;
                            
                            //System.out.println("CONFIGURED TRUE");
                        }

                        //IMROOT from root with configuration (CONTROLLARE SE SERVE, FORSE RIDONDANTE)
                        if (packetCode == 1 && senderId != ME.getId()) {
                            if (ME.isRoot()){
                        		ME.setIsRoot(false);
                        		ME.sendPLCRQ(receivePacket.getAddress());
                            }
                        }
                        
                        //DSCVRY from ME with configuration and riconf flag ON
                        if (packetCode == 0 && senderId == ME.getId() && configured==true && riconf==true) {
                        	//ME.setIp(receivePacket.getAddress());
                            System.out.println("[Starting] This host IP: "+ ME.getIp().getHostAddress());
                            
                            //TODO timeout da controllare
                            rcvSocket.setSoTimeout(500); //0.5 seconds timeout

                            //Wait for the IMROOT packet (if root exist) otherway set me as root
                            receivePacket = new DatagramPacket(receiveData, receiveData.length);
                            try {
                            	rcvSocket.receive(receivePacket);
                                packetCode = (int) receivePacket.getData()[0];
                                if (receivePacket.getData()[0]==1){ //If IMROOT received
                                	System.out.println("[Found] Root found with ID " + receivePacket.getData()[4]);
                                	ME.sendPLCRQ(receivePacket.getAddress()); //look for my new place
                    //                System.out.println("inviato PLCRQ");
                                    riconf=false;
                                    if (ME.isRoot())  //se per sbaglio sono root, torno normale (forse non serve)
                                    	ME.setIsRoot(false);
                                }
                            }catch(Exception e){
                            	//No response from root. I may be root
                            	//if I'm the right son, send another DSCVRY (search 2 times)
                            	if (root_attempt==0 && ME.getId()>ME.getDadId()){
                            		ME.sendDSCVRY();
                            		root_attempt=1;
                            	}else{
                            		//Set myself to root
                            		ME.setIsRoot(true);
                            		ME.setDad(0, "0.0.0.0");
                            		System.out.println("I'm ROOT");
                            		riconf=false;
                            		root_attempt=0;
                            	}
                            }
                            rcvSocket.setSoTimeout(0);
                        }
                        
                        //DSCVRY packet received and I'm ROOT send an IMROOT
                        if (packetCode == 0 && ME.isRoot() && senderId!= ME.getId()) {
                        	//TODO mandarlo in broadcast??
                            ME.sendIMROOT(senderIP, senderId);
                            configured=true;
                        }

            			// PLCRQ packet received
            			if (packetCode == 2 && senderId != ME.getId() ) {
            				senderId=receivePacket.getData()[4];
            				
            				System.out.println("[UDP]Received PLCRQ from ID " + senderId);
            				//Send an ACK to the sender
            				if (!ME.isRoot()){
            					ME.sendACKtoPLC(ME.getDadId());
            				//	System.out.println("ACK SENT TO "+ME.getDadId());
            				}
            				if (senderId > ME.getId() && ME.getRightId() == 0) {
            					// It's my right son
            					//take the IP from the packet
            					byte[] indirizzo = new byte[4];
            					indirizzo[0] = receivePacket.getData()[7];
            					indirizzo[1] = receivePacket.getData()[8];
            					indirizzo[2] = receivePacket.getData()[9];
            					indirizzo[3] = receivePacket.getData()[10];
            					ME.deleteRight();
            					//Set the node as my right son
            					ME.setRight(senderId, InetAddress.getByAddress(indirizzo));
            					//System.out.println("[Configuration]: Set ID " + senderId+ " as my right son");
            					ME.startGateInR();
            					System.out.println("[Configuration]: Set ID " + senderId+ " as my right son");
            					ME.sendSETFIELD(ME.getRightId(), 'D', ME.getId(), ME.getIp());

            					//Set timer check for right son
            					timer3= new Timer();
            					TimerTask task3= new TimerTask(){
										public void run(){
										int idTx2=0;
										try{
											ME.sendNodeOn(/*ME.getRightIp(),*/ ME.getRightId()/*, 2*/);
										
											//TODO timeout
											rcvSocketRight.setSoTimeout(2000);
											int packetCode=0;		
											while(packetCode!=7){
												DatagramPacket receivePacketRight = new DatagramPacket(receiveData, receiveData.length);
												rcvSocketRight.receive(receivePacketRight);
												idTx2 = (int) receivePacketRight.getData()[4];
													
												packetCode = (int) receivePacketRight.getData()[0];
												//Received ACK packet
												//if (packetCode == 7)
												//{
												//	//System.out.println("[UDP; UDP_PORT_ACK_RIGHT] ACK received from ID " + idTx2);
												//	break;
												//}
                               				}
										}catch(Exception e){
										//	System.out.println("NODO " + idTx2 + " IRRAGGIUNGIBILE");
											try{												
												ME.deleteRight();
												ME.setRightWeight(0, 'd');
												ME.printGraph();
												ME.sendWP(ME.getDadId());
											}catch(Exception a){}
											this.cancel();
										}
									}
								};
								timer3.scheduleAtFixedRate(task3, 4000, 4000);
								rcvSocketRight.setSoTimeout(0);
            				} else if (senderId < ME.getId() && ME.getLeftId() == 0) {
            					// It's my left son
            					//take the IP from the packet
            					byte[] indirizzo = new byte[4];
            					indirizzo[0] = receivePacket.getData()[7];
            					indirizzo[1] = receivePacket.getData()[8];
            					indirizzo[2] = receivePacket.getData()[9];
            					indirizzo[3] = receivePacket.getData()[10];
            					ME.deleteLeft();
            					//set the node as my left node
            					ME.setLeft(senderId, InetAddress.getByAddress(indirizzo));
            					
            					ME.startGateInL();
            					System.out.println("[Configuration]: Set ID " + senderId+ " as my left son");
            					//Send a SETFIELD to my new son to configure me as his dad
            					ME.sendSETFIELD(ME.getLeftId(), 'D', ME.getId(), ME.getIp());
            					
            					//Set timer check for left son
            					timer2= new Timer();
								
							    TimerTask task2=new TimerTask()
								{
									int idTx1=0;
									public void run(){
										try{
											ME.sendNodeOn(/*ME.getLeftIp(),*/ ME.getLeftId()/*, 1*/);
											//TODO timeout
											rcvSocketLeft.setSoTimeout(2000);
											int packetCode=0;
											while(packetCode!=7){
												DatagramPacket receivePacketLeft = new DatagramPacket(receiveData, receiveData.length);
												rcvSocketLeft.receive(receivePacketLeft);
												idTx1 = (int) receivePacketLeft.getData()[4];

												packetCode = (int) receivePacketLeft.getData()[0];
												//Received ACK packet
												//if (packetCode == 7)
												//{
												//	System.out.println("[UDP; UDP_PORT_ACK_LEFT] ACK received from ID " + idTx1);
												//	break;
												//}
						       				}
										}catch(Exception e){
										//	System.out.println("NODO " + idTx1 + " IRRAGGIUNGIBILE");
											try{												
												ME.deleteLeft();
												ME.setLeftWeight(0, 'd');
												ME.printGraph();
												ME.sendWP(ME.getDadId());
											}catch(Exception a){}
											this.cancel();
										}
									}
								};
								timer2.scheduleAtFixedRate(task2, 4000, 4000);
				
								rcvSocketLeft.setSoTimeout(0);
				
            					//ME.sendACK(ME.getDadId());
            					// if(ME.getRightId()==0)
            					// ME.setLeftWeight(1);
								
								
            				}else {
            					// It's not my son. Forward the packet to right or left
            					// ME.setWeight(ME.getWeight()+1);
            					if (receivePacket.getData()[4]>ME.getId())
            						ME.forward(ME.getRightId(),receivePacket.getData());
            					else
            						ME.forward(ME.getLeftId(),receivePacket.getData());
            					
            					//Start waiting for ACK
            					//TODO timeout
            					rcvSocket.setSoTimeout(1000); //0.5 seconds timeout
            					receivePacket = new DatagramPacket(receiveData, receiveData.length);
            					//TODO controllo che sia effettivamente un ack
                                try {
                                	rcvSocket.receive(receivePacket);
                                	packetCode = (int) receivePacket.getData()[0];
                                	}catch(Exception e){
                                		//Il nodo è saltato
                              //  		System.out.println("ERROR");
                                		if(senderId>ME.getId()){
                                			//Sostituisco il nodo saltato con il nuovo nodo in ingresso
                                			//TODO ATTENZIONE, NON PARTONO I TIMER IN QUESTO CASO!!!!!!!
                                			// It's my right son
                                			byte[] indirizzo = new byte[4];
                                			indirizzo[0] = receivePacket.getData()[7];
                                			indirizzo[1] = receivePacket.getData()[8];
                                			indirizzo[2] = receivePacket.getData()[9];
                                			indirizzo[3] = receivePacket.getData()[10];
                                			ME.setRight(senderId, InetAddress.getByAddress(indirizzo));
                                			System.out.println("[Configuration]: Set ID " + senderId+ " as my right son");
                                			ME.sendSETFIELD(ME.getRightId(), 'D', ME.getId(), ME.getIp());
                                		}
                                		if(senderId<ME.getId()){
                                			//Sostituisco il nodo saltato con il nuovo nodo in ingresso
                                			// It's my left son
                                			byte[] indirizzo = new byte[4];
                                			indirizzo[0] = receivePacket.getData()[7];
                                			indirizzo[1] = receivePacket.getData()[8];
                                			indirizzo[2] = receivePacket.getData()[9];
                                			indirizzo[3] = receivePacket.getData()[10];
                                			ME.setLeft(senderId, InetAddress.getByAddress(indirizzo));
                                			System.out.println("[Configuration]: Set ID " + senderId+ " as my left son");
                                			ME.sendSETFIELD(ME.getLeftId(), 'D', ME.getId(), ME.getIp());
                                			
                                			//timer2=new Timer();
            								//timer2.scheduleAtFixedRate(task2, 4000, 4000);

                                		}
                                	};
                                	rcvSocket.setSoTimeout(0);
                            }
            				ME.printGraph();
            				
            				
            				
            			}
                       
            			// TESTING
            			// Weight packet received
            			if (packetCode == 10 /*&& senderId != ME.getId()*/ && (senderId==ME.getLeftId() || senderId==ME.getRightId()) || senderId==ME.getId()) {
            				senderId=receivePacket.getData()[4];
            				boolean rotateCD = false;
            				int weight;
            	//			System.out.println("ARR: "+receivePacket.getData()[10]);
            				if (receivePacket.getData()[10] < 0)
            					weight = receivePacket.getData()[10] - 1;
            				else
            					weight = receivePacket.getData()[10] + 1;

            				if (senderId == ME.getLeftId())
            					ME.setLeftWeight(weight, 'L');
            				if (senderId == ME.getRightId())
            					ME.setRightWeight(weight, 'R');

            				if (receivePacket.getData()[10] < 0 && (Math.abs(ME.getLeftWeight() - ME.getRightWeight())==2)&&(ME.getRightWeight()==2 || ME.getLeftWeight()==2)) {
            		//			System.out.println("arrivato peso negativo");
            					rotateCD = true;
            				}
            				ME.printGraph();
            				//
            				//CONDIZIONE ROMPICOGLIONI
            				//
            				if (ME.isRoot() == false && (Math.abs(ME.getLeftWeight() - ME.getRightWeight()) != 2) && ME.getDadId()!=0/*&& ME.getWeight() != 2*/)
            					ME.sendWP(ME.getDadId());
            				

            				// CASE A
            				if ((ME.getLeftWeight() - ME.getRightWeight() >= 2)/*&&(ME.getRightWeight()==2 || ME.getLeftWeight()==2) */&& rotateCD == false) {
            					// invia a figlio sinistro mio padre come suo padre
           				System.out.println("\u001B[34mROTATION: CASE A\u001B[0m");
            					
            					ME.sendAVLR(ME.getLeftId(), 'A');
            					ME.sendSETFIELD(ME.getLeftId(), 'D', ME.getDadId(),	ME.getDadIp());
            					if (!ME.isRoot()) {
            						ME.sendSETFIELD(ME.getDadId(), '*', ME.getLeftId(),	ME.getLeftIp());
            					}
            					
            					else
            						ME.setIsRoot(false);
            					ME.deleteDad();
            					//Devo prendere il figlio destro e impostarlo come mio figlio sinistro
            					//ME.sendSETFIELD(ME.getLeftId(), 'R', ME.getId(), ME.getIp());
            					ME.sendSETFIELD(ME.getLeftId(), 'R', ME.getId(),ME.getIp());
            					ME.setDad(ME.getLeftId(), ME.getLeftIp());
            					//bloccare timer figlio sinistro (nuovo figlio sinistro è nullo)--------------------
            				//	System.out.println("Timer Figlio sinistro bloccato");
            					timer2.cancel();//------------------
            					//timer2.cancel();
            					
				          	ME.setDad(ME.getLeftId(), ME.getLeftIp());
						ME.deleteLeft();
            					ME.setLeftWeight(0, '-');
            			//		ME.setRightWeight(0, '-');
            			//		ME.sendSETFIELD(ME.getLeftId(), 'd', 0, ME.getIp());
            			//		ME.sendWP(ME.getDadId());

            				}

            				// CASE B
            				if (((ME.getLeftWeight() - ME.getRightWeight() <= -2)/*&&(ME.getRightWeight()==2 || ME.getLeftWeight()==2))*/	&& rotateCD == false)) {
            					System.out.println("\u001B[34mROTATION: CASE B\u001B[0m");
            					// invia a figlio sinistro mio padre come suo padre
						
            					ME.sendSETFIELD(ME.getRightId(), 'D', ME.getDadId(), ME.getDadIp());
            					if (!ME.isRoot()) {
            						ME.sendSETFIELD(ME.getDadId(), '*', ME.getRightId(), ME.getRightIp());
            					}

            					else
            						ME.setIsRoot(false);
            					ME.deleteDad();
            					ME.sendSETFIELD(ME.getRightId(), 'L', ME.getId(),ME.getIp());
            					ME.setDad(ME.getRightId(), ME.getRightIp());
            					//bloccare timer figlio destro (nuovo figlio destro è nullo)--------------------
            					timer3.cancel();
            					ME.deleteRight();
            			//		ME.setLeftWeight(0, '-');
            					ME.setRightWeight(0, '-');
            			//		ME.sendSETFIELD(ME.getLeftId(), 's', 0, ME.getIp());

            			//		ME.sendWP(ME.getDadId());

            				}
            				

            				// Case C
            				if (rotateCD == true && senderId == ME.getRightId()) {
            					//Make some changes in his sub-tree to have a B case
         //   					System.out.println("\u001B[34mROTATION: CASE C\u001B[0m");
            					ME.sendAVLR(ME.getRightId(), 'C');
            					
            				}

            				// Case D
            				if (rotateCD == true && senderId == ME.getLeftId()) {
            					//Make some changes in his sub-tree to have a A case
           // 					System.out.println("\u001B[34mROTATION: CASE D\u001B[0m");
            					ME.sendAVLR(ME.getLeftId(), 'D');
            				}

            			//	if(!ME.isRoot())
            			//		ME.sendWP(ME.getDadId());
            				ME.printGraph();

            			}
                        
                        //CLOSE packet received
                        if (packetCode == 4 && senderId!=ME.getId()){
            				senderId=receivePacket.getData()[4];

                                System.out.println("[UDP]Received CLOSE from ID "+senderId);
                                byte[] indirizzo = new byte[4];
                                indirizzo[0]=receivePacket.getData()[7];
                                indirizzo[1]=receivePacket.getData()[8];
                                indirizzo[2]=receivePacket.getData()[9];
                                indirizzo[3]=receivePacket.getData()[10];
                                
                                //If left child
                                if(receivePacket.getData()[4] == ME.getLeftId()){
    								ME.deleteLeft();
    								ME.setLeftWeight(0, 'd');
    								timer2.cancel();
    								ME.sendWP(ME.getDadId());
    								ME.printGraph();
    								
                                }//If right child
                                else if(receivePacket.getData()[4] == ME.getRightId()){
    								ME.deleteRight();
    								ME.setRightWeight(0, 's');
    								timer3.cancel();
    								ME.sendWP(ME.getDadId());
    								ME.printGraph();
                                }
                                //set new height: 3 possible cases. A: two children B: one child C: root close
                        }
                        
                        //Received PacketOnNode
                        if (packetCode == 6 && senderId!=ME.getId()){
            				senderId=receivePacket.getData()[4];

//                            System.out.println("[UDP]Received NodeON from ID "+senderId);
                            byte[] indirizzo = new byte[4];
                            indirizzo[0]=receivePacket.getData()[7];
                            indirizzo[1]=receivePacket.getData()[8];
                            indirizzo[2]=receivePacket.getData()[9];
                            indirizzo[3]=receivePacket.getData()[10];
                                
                            //ME.sendACK(senderIP, senderId);
                          //Send ACK to child
                            //if(receivePacket.getData()[11]==0)
							//	ME.sendACK(senderIP, senderId/*, 0*/);
							//Left leaf sends ACK to dad
							//else if(receivePacket.getData()[11]==1){
							//	ME.sendACK(senderIP, senderId/*, 1*/);
							//	System.out.println("ACK TO DAD");
							//}
                            //Right leaf sends ACK to dad
							//else{
							//	ME.sendACK(senderIP, senderId/*, 2*/);
                            if(senderId==ME.getDadId() || senderId==ME.getRightId() || senderId==ME.getLeftId())
                            	ME.sendACK(senderIP, senderId);
//								System.out.println("ACK TO DAD");
							
                            
                        }
                     // SETFIELD packet received
            			if (packetCode == 8 && senderId != ME.getId()) {
            				senderId=receivePacket.getData()[4];
            				int idfield = receivePacket.getData()[11];
            				byte[] indirizzo = new byte[4];
            				indirizzo[0] = receivePacket.getData()[14];
            				indirizzo[1] = receivePacket.getData()[15];
            				indirizzo[2] = receivePacket.getData()[16];
            				indirizzo[3] = receivePacket.getData()[17];
            				InetAddress ipfield = InetAddress.getByAddress(indirizzo);
            				char fieldtoset = (char) receivePacket.getData()[10];
            				if (fieldtoset == 'D') {
            					if(idfield!=ME.getId()){
            						ME.deleteDad();
            						ME.setDad(idfield, ipfield);
            						
            						//ATTENZIONE!!!!!!!!!
                    			ME.sendWP(ME.getDadId());
            					}
            					if (idfield == 0){
            						ME.setIsRoot(true);
            					}
            				}
            				if (fieldtoset == 'd') {
            					ME.setRightWeight(ME.getRightWeight()-1, 'd');
            				}
            				if (fieldtoset == 's') {
            					ME.setLeftWeight(ME.getLeftWeight()-1, 's');
            				}
            				if (fieldtoset == 'R') {
            					if (idfield!=ME.getId()){
            						ME.deleteRight();
            						ME.setRight(idfield, ipfield);
            					  if(idfield!=0){
            				
            						timer3= new Timer();
            						TimerTask task3= new TimerTask(){
										public void run(){
										int idTx2=0;
										try{
											ME.sendNodeOn(/*ME.getRightIp(),*/ ME.getRightId()/*, 2*/);
										
											//TODO timeout
											rcvSocketRight.setSoTimeout(2000);
											int packetCode=0;		
											while(packetCode!=7){
												DatagramPacket receivePacketRight = new DatagramPacket(receiveData, receiveData.length);
												rcvSocketRight.receive(receivePacketRight);
												idTx2 = (int) receivePacketRight.getData()[4];
													
												packetCode = (int) receivePacketRight.getData()[0];
												//Received ACK packet
												//if (packetCode == 7)
												//{
												//	//System.out.println("[UDP; UDP_PORT_ACK_RIGHT] ACK received from ID " + idTx2);
												//	break;
												//}
                               				}
										}catch(Exception e){
									//		System.out.println("NODO " + idTx2 + " IRRAGGIUNGIBILE");
											try{												
												ME.deleteRight();
												ME.setRightWeight(0, 'd');
												ME.printGraph();
												ME.sendWP(ME.getDadId());
											}catch(Exception a){}
											ME.printGraph();

											this.cancel();
										}
									}
								};
								timer3.scheduleAtFixedRate(task3, 4000, 4000);
								rcvSocketRight.setSoTimeout(0);
								}}
            				}
            				if (fieldtoset == 'L') {
            					if(idfield!=ME.getId()){
            						ME.deleteLeft();
            						ME.setLeft(idfield, ipfield);
            					//fare partire timer 2
            					if(idfield!=0){	
            					
            					timer2= new Timer();
								
							    TimerTask task2=new TimerTask()
								{
									int idTx1=0;
									public void run(){
										try{
											ME.sendNodeOn(/*ME.getLeftIp(),*/ ME.getLeftId()/*, 1*/);
											//TODO timeout
											rcvSocketLeft.setSoTimeout(2000);
											int packetCode=0;
											while(packetCode!=7){
												
												DatagramPacket receivePacketLeft = new DatagramPacket(receiveData, receiveData.length);
												rcvSocketLeft.receive(receivePacketLeft);
												idTx1 = (int) receivePacketLeft.getData()[4];

												packetCode = (int) receivePacketLeft.getData()[0];
												//Received ACK packet
												//if (packetCode == 7)
												//{
												//	System.out.println("[UDP; UDP_PORT_ACK_LEFT] ACK received from ID " + idTx1);
												//	break;
												//}
						       				}
										}catch(Exception e){
								//			System.out.println("NODO " + idTx1 + " IRRAGGIUNGIBILE");
											try{												
												ME.deleteLeft();
												ME.setLeftWeight(0, 'd');
												ME.printGraph();
												ME.sendWP(ME.getDadId());
												
											}catch(Exception a){}
											ME.printGraph();
											this.cancel();
										}
									}
								};
								timer2.scheduleAtFixedRate(task2, 4000, 4000);
								rcvSocketLeft.setSoTimeout(0);
            					///timer2.scheduleAtFixedRate(task2,0,5000);
						}}
            				}
            				if (fieldtoset == '*') {
            					if (senderId == ME.getLeftId()) {
            						if(idfield!=ME.getId()){
            							ME.deleteLeft();
            							ME.setLeft(idfield, ipfield);
            						}
            					}
            					if (senderId == ME.getRightId()) {
            						if(idfield!=ME.getId()){
            							ME.deleteRight();
            							ME.setRight(idfield, ipfield);
            						}
            					}
            				}
            				System.out.println("SETFIELDreceived code: "+fieldtoset+ " - senderId: "+receivePacket.getData()[4]);

            				ME.printGraph();
            			//	if (!ME.isRoot())
            					//ME.sendWP(ME.getDadId());
            			}
            			
            			//AVLR packet reveived
            			if (packetCode == 9 && senderId != ME.getId()) {
            				char type = (char) receivePacket.getData()[10];
            				if (type == 'A') {
            					ME.deleteRight();
            					ME.sendSETFIELD(ME.getDadId(), 'L', ME.getRightId(), ME.getRightIp());
            					ME.setRight(ME.getDadId(), ME.getDadIp());
            					
            				}
            				if (type == 'C') {
            					System.out.println("Executing double rotation for case C");
            					
            					ME.sendSETFIELD(ME.getLeftId(), 'D', ME.getDadId(),	ME.getDadIp());
            					ME.sendSETFIELD(ME.getLeftId(), 'R', ME.getId(), ME.getIp());
            					ME.sendSETFIELD(ME.getDadId(), 'R', ME.getLeftId(),	ME.getLeftIp());
            					ME.deleteDad();
            					ME.setDad(ME.getLeftId(), ME.getLeftIp());
            					ME.deleteLeft();
            					ME.setLeftWeight(0, '-');
            					//ME.setRightWeight(0, '-');
            					ME.sendWP(ME.getDadId());

            				}
            				if (type == 'D') {
            					System.out.println("Executing double rotation for case D");
            					ME.deleteDad();
            					ME.sendSETFIELD(ME.getRightId(), 'D', ME.getDadId(),ME.getDadIp());
            					ME.sendSETFIELD(ME.getRightId(), 'L', ME.getId(),ME.getIp());
            					ME.sendSETFIELD(ME.getDadId(), 'L', ME.getRightId(),ME.getRightIp());
            					ME.setDad(ME.getRightId(), ME.getRightIp());
            					ME.deleteRight();
            				//	ME.setLeftWeight(0, '-');
            					ME.setRightWeight(0, '-');
            					ME.sendWP(ME.getDadId());

            				}
            				ME.printGraph();
            			}
            			if (packetCode == 40 && senderId != ME.getId()) {
            				int code = (int) receivePacket.getData()[10];
            				ME.sendECHO(senderId, code);
            			}
            			if (packetCode == 41 && senderId != ME.getId()){
            				
            				java.util.Date date = new java.util.Date();
            				System.out.println("Received ECHO "+new java.sql.Timestamp(date.getTime()));
            				
            				
            			}
            			
            			
            			if(packetCode == 50  && senderId != ME.getId()){
            				int dest=receivePacket.getData()[7];
            				int nack=receivePacket.getData()[11];
            			//	System.out.print("Received ACK from "+senderId+" to "+dest+" with number "+nack);
            				System.out.print(" \r");
            				boolean LS;
            	    		if(ME.getId()<ME.getDadId()){
            	    			//I'm a left son
            	    			LS=true;
            	    		}else
            	    			LS=false;
            				if(dest==ME.getId()){
            					if(senderId>ME.getId() && ((senderId<ME.getDadId() && LS==true)||(senderId>ME.getDadId() && LS==false) || ME.getDadId()==0) && ME.getRightId()!=0){
            					    // ME.ROut.ack(nack);
            					}
            					if(senderId<ME.getId() && ((senderId<ME.getDadId() && LS==true) || (senderId>ME.getDadId() && LS==false) || ME.getDadId()==0) && ME.getLeftId()!=0){
            					//	ME.LOut.ack(nack);
            					
            					}
            					if((senderId>=ME.getDadId() && LS==true)||(senderId<=ME.getDadId() && LS==false) || (senderId<=ME.getDadId() && senderId<=ME.getId() && ME.getLeftId()==0 ) || (senderId>=ME.getDadId() && senderId>=ME.getId() && ME.getRightId()==0 )&& ME.getDadId()!=0){
            						//ME.DOut.ack(nack);
            					}
            					ME.D.ack(nack);
            				
            						
            				}else
            					ME.forward(receivePacket.getData());
            			}
            		}
            }

 
        	public static void attachShutDownHook() {
        		Runtime.getRuntime().addShutdownHook(new Thread() {
        			public void run(){
        				try{
        					System.out.println("Machine shutdown");
        					//tell its shutdown to dad
        					if(!ME.isRoot()) 
        						ME.sendCLOSE(ME.getDadId());
        					//tell its shutdown to LeftSon
        					if(ME.getLeftId()!=0)
								ME.sendCLOSE(ME.getLeftId());
							//tell its shutdown to RightSon
							if(ME.getRightId()!=0)
								ME.sendCLOSE(ME.getRightId());
        					
        						
        					
        				}catch(Exception e){}
        			}	
        		});
			}
        	
 }



